﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Permissions;
using System.Threading;

namespace Needle.Utils.Parser
{
    /// <summary></summary>
    public abstract class StreamTokenizer<T> : MarshalByRefObject, IEnumerable<T>, IDisposable
        where T : Token
    {
        #region Constructors

        /// <summary></summary>
        public StreamTokenizer(Stream s)
            : this(s, true)
        { }

        /// <summary></summary>
        public StreamTokenizer(Stream s, bool ownsStream)
        {
            Ensure.NotNull(s, "s");
            Ensure.IsTrue(s.CanRead, "s", "s must be a readable stream.");
            _stream = s;
            _ownsStream = ownsStream;
            _asyncActiveCount = 1;
        }

        #endregion

        #region Properties...

        private bool _ownsStream;
        /// <summary></summary>
        protected bool OwnsStream
        {
            get { return _ownsStream; }
        }

        private Stream _stream;
        /// <summary></summary>
        protected Stream Stream
        {
            get { return _stream; }
        }

        /// <summary></summary>
        public bool CanSeek
        {
            get { return _stream.CanSeek; }
        }

        /// <summary></summary>
        public virtual long Position
        {
            get { return _stream.Position; }
            set
            {
                if (Interlocked.CompareExchange(ref _asyncActiveCount, 0, 0) != 1)
                    throw new InvalidOperationException("Cannot change position, async read in progress or instance was disposed.");
                _stream.Position = value;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary></summary>
        ~StreamTokenizer()
        {
            Dispose(false);
        }

        /// <summary></summary>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_asyncActiveEvent != null)
                    CloseAsyncActiveEvent(Interlocked.Decrement(ref this._asyncActiveCount));
                if (_ownsStream)
                    _stream.Dispose();
         
            }
        }

        /// <summary></summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary></summary>
        public abstract T Read();

        [NonSerialized]
        private Func<T> _asyncRead;
        [NonSerialized]
        private int _asyncActiveCount;
        [NonSerialized]
        private AutoResetEvent _asyncActiveEvent;

         /// <summary></summary>
        [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)]
       public IAsyncResult BeginRead(AsyncCallback callback, object state)
        {
            Interlocked.Increment(ref _asyncActiveCount);
            Func<T> read = this.Read;
            if (_asyncActiveEvent == null)
            {
                lock (this)
                {
                    if (_asyncActiveEvent == null)
                        _asyncActiveEvent = new AutoResetEvent(true);
                }
            }
            _asyncActiveEvent.WaitOne();
            _asyncRead = read;
            return read.BeginInvoke(callback, state);
        }

        /// <summary></summary>
        public T EndRead(IAsyncResult asyncResult)
        {
            Ensure.NotNull(asyncResult, "asyncResult");
            Ensure.NotNull(_asyncRead, "asynResult", "Invalid operation, EndRead called multiple times.");
             
            T token = null;
            try
            {
                token = _asyncRead.EndInvoke(asyncResult);
            }
            finally
            {
                _asyncRead = null;
                _asyncActiveEvent.Set();
                CloseAsyncActiveEvent(Interlocked.Decrement(ref _asyncActiveCount));
            }
            return token;
        }

        private void CloseAsyncActiveEvent(int asyncActiveCount)
        {
            if (_asyncActiveEvent != null && asyncActiveCount == 0)
            {
                _asyncActiveEvent.Close();
                _asyncActiveEvent = null;
            }
        }

        #region IEnumerable<T> Members

        /// <summary></summary>
        public IEnumerator<T> GetEnumerator()
        {
            T token;
            while (true)
            {
                token = Read();
                if (token == null)
                    yield break;
                yield return token;
            }
        }

        /// <summary></summary>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    
    }

    /// <summary></summary>
    public abstract class Token
    {

        /// <summary></summary>
        public Token(long pos)
        {
            _position = pos;
        }

        private readonly long _position;
        /// <summary></summary>
        public long Position
        {
            get { return _position; }
        }

    }

}
